package org.test.client.forcedirect;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ForceDirectLayout {

	private int iterateTimes = 300;

	/*
	 * 1. 随机分布初始节点位置； 2. 计算每次迭代局部区域内两两节点间的斥力所产生的单位位移（一般为正值）； 3.
	 * 计算每次迭代每条边的引力对两端节点所产生的单位位移（一般为负值）； 4. 步骤 2、3
	 * 中的斥力和引力系数直接影响到最终态的理想效果，它与节点间的距离、 节点在系统所在区域的平均单位区域均有关，需要开发人员在实践中不断调整； 5.
	 * 累加经过步骤 2、3 计算得到的所有节点的单位位移； 6. 迭代 n 次，直至达到理想效果。
	 */
	public void Layout(List<FDNode> nodes, List<FDEdge> edges) {

		long before = System.currentTimeMillis();
		
		// 4.反复2,3步 迭代300次
		for (int i = 0; i < iterateTimes; i++) {
			nodes = calcPos(nodes, edges);
		}
		
		long after = System.currentTimeMillis();
		System.out.println("排序共用" + (after-before) + "ms");
	}

	public List<FDNode> calcPos(List<FDNode> nodes, List<FDEdge> edges) {

		// 2计算每次迭代局部区域内两两节点间的斥力所产生的单位位移（一般为正值）
		int area = TestMain.mapwidth * TestMain.mapheight;
		double k = Math.sqrt(area / (double) nodes.size()) ;
		double diffx, diffy, diff;

		Map<String, Double> dispx = new HashMap<String, Double>();
		Map<String, Double> dispy = new HashMap<String, Double>();

		int ejectfactor = 6;
		
		for (int v = 0; v < nodes.size(); v++) {
			dispx.put(nodes.get(v).getId(), 0.0);
			dispy.put(nodes.get(v).getId(), 0.0);
			for (int u = 0; u < nodes.size(); u++) {
				if (u != v) {
					diffx = nodes.get(v).getX() - nodes.get(u).getX();
					diffy = nodes.get(v).getY() - nodes.get(u).getY();

					diff = Math.sqrt(diffx * diffx + diffy * diffy);

					if (diff < 30)
						ejectfactor = 5;

					if (diff > 0 && diff < 250) {
						//f=g*m1*m2/r*r 库仑定律
						String id = nodes.get(v).getId();
						dispx.put(id, dispx.get(id) + diffx / diff * k * k / diff * ejectfactor);
						dispy.put(id, dispy.get(id) + diffy / diff * k * k / diff * ejectfactor);
					}
				}
			}
		}
		
		
		// 3. 计算每次迭代每条边的引力对两端节点所产生的单位位移（一般为负值）
		int condensefactor = 3;
		FDNode visnodeS = null, visnodeE = null;

		for (int e = 0; e < edges.size(); e++) {
			FDEdge fdEdge = edges.get(e);
			visnodeS = fdEdge.getSrcNode();
			visnodeE = fdEdge.getDstNode();
			String eStartID = visnodeS.getId();
			String eEndID = visnodeS.getId();

			diffx = visnodeS.getX() - visnodeE.getX();
			diffy = visnodeS.getY() - visnodeE.getY();
			diff = Math.sqrt(diffx * diffx + diffy * diffy);

			dispx.put(eStartID, dispx.get(eStartID) - diffx * diff / k* condensefactor);
			dispy.put(eStartID, dispy.get(eStartID) - diffy * diff / k	* condensefactor);
			dispx.put(eEndID, dispx.get(eEndID) + diffx * diff / k	* condensefactor);
			dispy.put(eEndID, dispy.get(eEndID) + diffy * diff / k	* condensefactor);
		}

		// set x,y
		int maxt = 4, maxty = 3;
		for (int v = 0; v < nodes.size(); v++) {
			FDNode node = nodes.get(v);
			Double dx = dispx.get(node.getId());
			Double dy = dispy.get(node.getId());

			int disppx = (int) Math.floor(dx);
			int disppy = (int) Math.floor(dy);
			if (disppx < -maxt)
				disppx = -maxt;
			if (disppx > maxt)
				disppx = maxt;
			if (disppy < -maxty)
				disppy = -maxty;
			if (disppy > maxty)
				disppy = maxty;

			node.setX((node.getX() + disppx));
			node.setY((node.getY() + disppy));
		}
		return nodes;

	}

}
